<?php

namespace App\Common;
use App\Models\Member\Store;
use App\Models\Member\PayConfig;
use App\Models\Member\AccessToken;
use App\Models\Member\User;
use Http, Config, Auth;

class WechatHandler
{

    /**
     * 绑定公众号域名
     *
     * @param $data request对象
     * @return 返回验证结果
     **/
    public static function bindHost($data)
    {
        $timestamp = $data['timestamp'];
        $nonce = $data['nonce'];
        $token = 'weixin';
        $signature = $data['signature'];
        $array = array($timestamp,$nonce,$token);
        $tmpstr = implode('',$array);
        $tmpstr = sha1($tmpstr);
        if ($tmpstr == $signature) {
            return $data['echostr'];
        } else {
            return false;
        }
    }

    /**
     * 将微信传入过来的xml转化为arr
     *
     * @param $data request对象
     * @return 返回xml转化后的arr
     **/
    public static function xmlToArr($data)
    {
        $xml = $data->getContent();
        if (empty($xml)) {
            return [];
        }

        return (array)simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA);
    }

    /**
     * 通过open_id和store_id直接进行用户数据的写入
     *
     * @param $openId 用户open_id 改为data 用户绑定关系注册信息 
     * @param $storeId 店铺id
     * @param $type 调用微信登陆的来源（'subscribe', 'login', 'bind'）
     * @param $code 当type为login或bind的时候需要传code用来获取未关注用户信息
     * @param $parentId 当type为bind的时候 需要传推荐人id 默认为0
     * @return array()
     **/
    public static function wxLogin($data, $storeId, $type, $code = '', $parentId = 0)
    {
        $user = new User;
        $wxConfig = self::getConfig($storeId);

        if ($type == 'subscribe') {
            // $accessToken = self::getAccessToken($storeId, $wxConfig);
            // $userInfo = self::getSubscribeUserInfo($openId, $accessToken);
            // $user->subscribeAndWxlogin($storeId, $userInfo, 'subscribe');
            return;
        }

        $oauthData = self::getOauthAccessToken($wxConfig, $code);
        if (isset($oauthData['errcode'])) return ['res' => false, 'info' => '无效code'];

        $userInfo = self::getOauthUserInfo($oauthData);

        if ($type == 'login') {
            $res = $user->subscribeAndWxlogin($storeId, $userInfo, 'login');
            return $res;
        }
        if ($type == 'bind') {
            $res = $user->bind($storeId, $parentId, $userInfo,$data);
            return $res;
        }
    }

    /**
     * 获取已关注用户信息
     *
     * @return array()
     **/
    private static function getSubscribeUserInfo($openId, $accessToken)
    {
        $url = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=$accessToken&openid=$openId&lang=zh_CN";
        $data = Http::get($url)->json();

        return $data;
    }

    /**
     * 获取oauth授权用户信息
     *
     * @return array()
     **/
    private static function getOauthUserInfo($oauthData)
    {
        $url = "https://api.weixin.qq.com/sns/userinfo?access_token={$oauthData['access_token']}&openid={$oauthData['openid']}&lang=zh_CN";
        $data = Http::get($url)->json();

        return $data;
    }

    /**
     * 根据店铺id获取access_token 若不存在或过期则生成或更新access_token
     *
     * @param $storeId 店铺id
     * @param $config 公众号配置array
     * @return array
     **/
    public static function getAccessToken($storeId, $wxConfig)
    {
        $data = AccessToken::where('store_id', $storeId)->get()->toArray();
        if (empty($data)) {
            // 不存在则生成并插入
            $responseResult = self::curlAccessToken($wxConfig);
            $accessTokenModel = new AccessToken;
            $result = $accessTokenModel->insertData($storeId, $responseResult);
            return $result['access_token'];
        }

        // 判断是否过期 如果未过期则直接返回 过期则将原有的修改后再返回
        $accessToken = $data[0];

        if (date("Y-m-d H:i:s") > $accessToken['expire_at']) {
            // token 虚重新获取并修改记录
            $responseResult = self::curlAccessToken($wxConfig);
            $accessTokenModel = new AccessToken;
            $result = $accessTokenModel->updateData($accessToken['id'], $responseResult);
            return $result['access_token'];
        }

        return $accessToken['access_token'];
    }

    /**
     * 根据oauth授权code获取access_token 此接口无使用上线 实时获取
     *
     * @param $code 授权code
     * @return array
     **/
    private static function getOauthAccessToken($wxConfig, $code)
    {
        $url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid={$wxConfig['app_id']}&secret={$wxConfig['app_secret']}&code={$code}&grant_type=authorization_code";
        $oauthData = Http::get($url)->json();

        return $oauthData;
    }

    /**
     * 根据微信配置抓取accessToken
     *
     * @param $config 公众号配置array
     * @return array [access_token, expires_in]
     **/
    private static function curlAccessToken($wxConfig)
    {
        $url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={$wxConfig['app_id']}&secret={$wxConfig['app_secret']}";
        $data = Http::get($url)->json();

        return $data;
    }

    /**
     * 根据店铺id获取微信配置
     *
     * @param $storeId 店铺id
     * @return array
     **/
    private static function getConfig($storeId)
    {
        return PayConfig::where('store_id', $storeId)->first()->toArray();
    }

    /**
     * 验证店铺是否存在
     *
     * @param $storeId 店铺id
     * @return boolean
     **/
    public static function checkStore($storeId)
    {
        $store = Store::where('id', $storeId)->where('status', 1)->first();

        if (empty($store)) return false;
        return true;
    }

    /**
     * 获取用户授权链接
     *
     * @param $storeId 店铺id
     * @return array
     **/
    public static function getOauthUrl($storeId, $redirectType)
    {
        if (!self::checkStore($storeId)) return ['res' => false];
        $redirect = Config::get('app.web_url').$redirectType;
        $redirect = urlencode($redirect);
        $wxConfig = self::getConfig($storeId);
        $url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid={$wxConfig['app_id']}&redirect_uri={$redirect}&response_type=code&scope=snsapi_userinfo#wechat_redirect";

        $store_name = Store::where('id', $storeId)->first()->title;

        return ['res' => true, 'url' => $url, 'store_name' => $store_name];
    }

    public static function initSdk($url)
    {
        $storeId = Auth::user()->store_id;
        $wxConfig = self::getConfig($storeId);

        $time = time();
        $nonceStr = self::getNonceStr();
        $ticket = self::getJsapiTicket($storeId, $wxConfig);
        $data = [
            'timestamp' => $time,
            'url' => $url,
            'noncestr' => $nonceStr,
            'jsapi_ticket' => $ticket
        ];
        $signature = self::getSignature($data);
        $data['signature'] = $signature;
        $data['appid'] = $wxConfig['app_id'];

        return $data;
    }

    private static function getNonceStr()
    {
        $chars = "abcdefghijklmnopqrstuvwxyz0123456789";
        $str ="";
        for ( $i = 0; $i < 32; $i++ ) {
            $str .= substr($chars, mt_rand(0, strlen($chars)-1), 1);
        }
        return $str;
    }

    private static function getJsapiTicket($storeId, $wxConfig)
    {
        $accessToken = self::getAccessToken($storeId, $wxConfig);

        $url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token={$accessToken}&type=jsapi";
        $data = Http::get($url)->json();

        return $data['ticket'];
    }

    private static function getSignature($data)
    {
        ksort($data);
        $buff = "";
		foreach ($data as $k => $v)
		{
			if($k != "sign"){
				$buff .= $k . "=" . $v . "&";
			}
		}

		$buff = trim($buff, "&");
        $buff = sha1($buff);
		return $buff;
    }

    // 统一下单接口
    public static function makeWxOrder($data)
    {
    	$requestUrl = $data['url'];
        $wxConfig = self::getConfig($data['storeId']);
        $openId = $data['openId'];

        $appId = $wxConfig['app_id'];
        $mchId = $wxConfig['merchant_id'];
        $paySecret = $wxConfig['pay_secret'];
        $body = $data['body'];
        $outTradeNo = $data['payOrderNum'];
        $totalFee = $data['totalFee'];
        $spbillCreateIp = $_SERVER['REMOTE_ADDR'];
        $notifyUrl = $data['notifyUrl'];
        $tradeType = $data['tradeType'];
        $nonceStr = self::getNonceStr();
        $str = "appid=$appId&body=$body&mch_id=$mchId&nonce_str=$nonceStr&notify_url=$notifyUrl&openid=$openId&out_trade_no=$outTradeNo&sign_type=MD5&spbill_create_ip=$spbillCreateIp&total_fee=$totalFee&trade_type=$tradeType&key=$paySecret";
        $sign = strtoupper(md5($str));

        $url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
        $data = "
        <xml>
          <appid>$appId</appid>
          <body>$body</body>
          <mch_id>$mchId</mch_id>
          <nonce_str>$nonceStr</nonce_str>
          <notify_url>$notifyUrl</notify_url>
          <openid>$openId</openid>
          <out_trade_no>$outTradeNo</out_trade_no>
          <spbill_create_ip>$spbillCreateIp</spbill_create_ip>
          <total_fee>$totalFee</total_fee>
          <trade_type>JSAPI</trade_type>
          <sign_type>MD5</sign_type>
          <sign>$sign</sign>
        </xml>";

        $response = Http::withHeaders([
            'headers' => [
                'Content-Type' => 'text/xml; charset=UTF8',
            ],
        ])->send("post", $url, [
            'body' => $data
        ]);
        $xml = $response->getBody();
        $responseData = new \SimpleXMLElement($xml);
        $prepayId = (string)$responseData->prepay_id;
        $newNonceStr = self::getNonceStr();
        $time = (string)time();
        $package = "prepay_id=$prepayId";
        $payStr = "appId=$appId&nonceStr=$newNonceStr&package=$package&signType=MD5&timeStamp=$time&key=$paySecret";
        $paySign = strtoupper(md5($payStr));
        $res = self::initSdk($requestUrl);
        $res['timestamp'] = $time;
        $res['package'] = $package;
        $res['noncestr'] = $newNonceStr;
        $res['paySign'] = $paySign;
        $res['pay_order_num'] = $outTradeNo;
        return $res;
    }
}
